package CastleDefense;

import CastleDefense.Managers.*;
import CastleDefense.Managers.GameManager.Difficulty;
import CastleDefense.units.*;
import java.awt.BasicStroke;
import java.awt.Button;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Panel;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.vecmath.Vector2d;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import CastleDefense.units.PlacedUnit.StateEffect;
import java.awt.event.*;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * The main hook of our game. This class with both act as a manager
 * for the display and central mediator for the game logic. 
 * 
 * Display management will consist of a loop that cycles round all
 * Enemies in the game asking them to move and then drawing them
 * in the appropriate place. With the help of an inner class it
 * will also allow the player to control the main ship.
 * 
 * As a mediator it will be informed when Enemies within our game
 * detect events (e.g. aliens killed, played died) and will take
 * appropriate game actions.
 * 
 * @author Kevin Glass
 */
public class Game extends Canvas
{
    /**
	 * 
	 */
    private static final long serialVersionUID = 1L;
    private JFrame container   = new JFrame("Castle Defense");
    private JPanel panel       = (JPanel) container.getContentPane();

    private   static int boardW = 1134;
    private   static int boardH = 984;
    
    protected int mouseX = 0;
    protected int mouseY = 0;
    private   int w = 0;
    private static BufferStrategy strategy;
    private static boolean gameRunning  = false;
    private static boolean gamePaused   = false;

    protected static ArrayList<Enemy> Enemies        = new ArrayList<Enemy>();
    protected static ArrayList<Enemy> notPsnEnemies  = new ArrayList<Enemy>();
    protected static ArrayList<Enemy> notSlwEnemies  = new ArrayList<Enemy>();
    private static ArrayList<Enemy>   removeList     = new ArrayList<Enemy>();
    private static int enemyCount;

    private static Enemy hoverMob;
    private UnitHunter   netter;
    private UnitTrap     catapult;
    private PlacedUnit   hoverUnit;
    private boolean      hover   = false;

    private boolean helpMode     = false;
    private boolean sellMode     = false;
    private boolean normalMouse  = true;
    private boolean attachedUnit = false;
    private boolean attachedTrap = false;
    private boolean attachedRock = false;
    private boolean attachedNet  = false;

    private boolean showHPbars   = true;
    private boolean showRange    = false;
    private boolean showHelpBox  = false;
    
    private String difficulty    = "easy";
    private String helpText      = "";
    
    /** Time before the next wave of monsters starts, in Seconds*/
    private int waveTime         = 140;
    //starting gold amount
    private static int gold      = 20250;

    private final int OFFSET     = 0;
    private final int SPACE      = 42;

    private ArrayList<Ground>       grounds     = new ArrayList<Ground>();
    private ArrayList<Wall>         walls       = new ArrayList<Wall>();
    private ArrayList<Area>         areas       = new ArrayList<Area>();
    private ArrayList<Button>       buttons     = new ArrayList<Button>();
    private ArrayList<AttachedUnit> units       = new ArrayList<AttachedUnit>();
    private static List<PlacedUnit> placedUnits = new CopyOnWriteArrayList<PlacedUnit>();
    private static List<Projectile> Projectiles = new ArrayList<Projectile>();

    private Panel   hoverPanel         = new Panel();

    private JButton btnChangeKey1      = new JButton();
    private JButton btnChangeKey2      = new JButton();

    private static  JButton btnReset2  = new JButton();

    private JButton btnStart           = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btnStartImg.PNG")));
    private JButton btnHelp            = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btnHelpImg.PNG")));
    private JButton btnSell            = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btnSellImg.PNG")));
    private JButton btnReset           = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btnResetImg.PNG")));
    //archery components
    private Panel   archeryPanel       = new Panel();
    private JButton btnUArcher         = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitARCHER.PNG")));
    private JButton btnUBlowgunist     = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitBLOWGUNIST.PNG")));
    private JButton btnUSpearThrower   = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitSPEARTHROWER.PNG")));
    private JButton btnUBallista       = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitBALLISTA.PNG")));
    private JButton btnUpgradeArchery  = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btn_UpgradeArchery.PNG")));
    //magic components
    private Panel   magicPanel         = new Panel();
    private JButton btnUMage           = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitFIREMAGE.PNG")));
    private JButton btnUIceSorcerer    = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitICESORCERER.PNG")));
    private JButton btnULightDruid     = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitLIGHTDRUID.PNG")));
    private JButton btnUEarthElemental = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitEARTHELEMENTAL.PNG")));
    private JButton btnUpgradeMagic    = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btn_UpgradeMagic.PNG")));
    //trap components
    private Panel   trapPanel          = new Panel();
    private JButton btnUSpikes         = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitSPIKE.PNG")));
    private JButton btnUCatapult       = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitCATAPULT.PNG")));
    private JButton btnUDeathSpike     = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitDEATHSPIKE.PNG")));
    private JButton btnUTNT            = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btn_template.PNG")));
    private JButton btnUpgradeTraps    = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btn_UpgradeTraps.PNG")));
    //hunter components
    private Panel   hunterPanel        = new Panel();
    private JButton btnUNetter         = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitNETTER.PNG")));
    private JButton btnULancer         = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitLANCER.PNG")));
    private JButton btnUGorillas       = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitGORILLAS.PNG")));
    private JButton btnUDragonSlayer   = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/unitDRAGONSLAYER.PNG")));
    private JButton btnUpgradeHunters  = new JButton(new ImageIcon(this.getClass().getResource("sprites/buttons/btn_UpgradeHunters.PNG")));
    
    protected StateManager sm;

    //game lists
    protected static WaveTimer   WaveTimer   = new WaveTimer();
    //protected static WaveManager GameWaves   = new WaveManager();
    protected static WaveList    WaveList    = new WaveList();
    protected static MonsterList MonsterList = new MonsterList();
    protected static HelpList    HelpList    = new HelpList();
    
    private AttachedUnit lastUsed;
    
    private MouseCursorListener   MCL = new MouseCursorListener();
    private MouseMovementListener MML = new MouseMovementListener();
    private KeyboardListener      KBL = new KeyboardListener();
    
    private static final String MAP =
              "#    ################\n"
            + "#    ################\n"
            + "#    ##             #\n"
            + "#    ##             #\n"
            + "#    ##             #\n"
            + "#    ##             #\n"
            + "#    ##    #####    #\n"
            + "#    ##    #####    #\n"
            + "#    ##    #####    #\n"
            + "#    ##    #####    #\n"
            + "#    ##    #####    #\n"
            + "#    ##        #    #\n"
            + "#    ##        #    #\n"
            + "#    ##        #    #\n"
            + "#    ##        #    #\n"
            + "#    ######    #    #\n"
            + "#    ######    #    #\n"
            + "#              #    #\n"
            + "#              #    #\n"
            + "#              #    #\n"
            + "#              #    #\n"
            + "################....#\n"
            + "#####################\n";

    /**
     * Construct our game and set it running.
     */
    public Game() throws JAXBException, FileNotFoundException
    {
        this.addMouseListener(MCL);
        this.addMouseMotionListener(MML);
        this.addKeyListener(KBL);
        
        container.setSize(boardW, boardH);
        Image icon = new ImageIcon(this.getClass().getResource("sprites/CD.PNG")).getImage();
        container.setIconImage(icon);

        // get hold the content of the frame and set up the resolution of the game
        panel.setPreferredSize(new Dimension(boardW,boardH));
        panel.setLayout(null);
        panel.setBackground(Color.black);

        // setup our canvas size and put it into the content of the frame
        setBounds(0,0,boardW,boardH);

        //MENU BUTTONS
        btnStart.setBackground(Color.black);
        btnHelp.setBackground(Color.black);
        btnSell.setBackground(Color.black);
        btnReset.setBackground(Color.black);

        //add menu buttons
        panel.add(btnStart);
        panel.add(btnHelp);
        panel.add(btnSell);
        panel.add(btnReset);

        btnChangeKey1.setMnemonic(KeyEvent.VK_1);

        //Add archery controls
        archeryPanel.setSize(new Dimension(257,174));
        archeryPanel.setBounds(882, 179, 0, 0);
        archeryPanel.setLayout(null);
        archeryPanel.setBackground(new Color(0,0,0,0));
        archeryPanel.add(btnUArcher);
        archeryPanel.add(btnUBlowgunist);
        archeryPanel.add(btnUSpearThrower);
        archeryPanel.add(btnUBallista);
        //add upgrade buttons
        archeryPanel.add(btnUpgradeArchery);
        //add archery panel
        panel.add(archeryPanel);

        //Add magic controls
        magicPanel.setSize(new Dimension(257,174));
        magicPanel.setBounds(882, 359, 0, 0);
        magicPanel.setLayout(null);
        magicPanel.setBackground(new Color(0,0,0,0));
        magicPanel.add(btnUMage);
        magicPanel.add(btnUIceSorcerer);
        magicPanel.add(btnULightDruid);
        magicPanel.add(btnUEarthElemental);
        //add upgrade buttons
        magicPanel.add(btnUpgradeMagic);
        //add magic panel
        panel.add(magicPanel);

        //Add trap controls
        trapPanel.setSize(new Dimension(257,174));
        trapPanel.setBounds(882, 539, 0, 0);
        trapPanel.setLayout(null);
        trapPanel.setBackground(new Color(0,0,0,0));
        trapPanel.add(btnUSpikes);
        trapPanel.add(btnUCatapult);
        trapPanel.add(btnUDeathSpike);
        trapPanel.add(btnUTNT);
        //add upgrade buttons
        trapPanel.add(btnUpgradeTraps);
        //add magic panel
        panel.add(trapPanel);

        //Add hunter controls
        hunterPanel.setSize(new Dimension(257,174));
        hunterPanel.setBounds(882, 719, 0, 0);
        hunterPanel.setLayout(null);
        hunterPanel.setBackground(new Color(0,0,0,0));
        hunterPanel.add(btnUNetter);
        hunterPanel.add(btnULancer);
        hunterPanel.add(btnUGorillas);
        hunterPanel.add(btnUDragonSlayer);
        //add upgrade buttons
        hunterPanel.add(btnUpgradeHunters);
        //add magic panel
        panel.add(hunterPanel);

        panel.add(btnReset2);
        
        panel.add(hoverPanel);
        //this :<>:
        panel.add(this);

        // Tell AWT not to bother repainting our canvas since we're
        // going to do that our self in accelerated mode
        setIgnoreRepaint(true);

        // finally make the window visible
        container.pack();
        container.setLocationRelativeTo(null);
        container.setResizable(false);
        container.setVisible(true);

        // add a listener to respond to the user closing the window. If they
        // do we'd like to exit the game
        container.addWindowListener(new WindowAdapter() 
        {
            @Override
            public void windowClosing(WindowEvent e) 
            {
                    System.exit(0);
            }
        });

        // request the focus so key events come to us
        requestFocus();

        // create the buffering strategy which will allow AWT
        // to manage our accelerated graphics
        createBufferStrategy(2);
        strategy = getBufferStrategy();

        startGame();
        loadXMLdata();
    }

    //DRAWS OUR MENU
    private void initMenu()
    {
        archeryPanel.setBounds(882, 179, 257, 174);
        magicPanel.setBounds(882, 359, 257, 174);
        trapPanel.setBounds(882, 539, 257, 174);
        hunterPanel.setBounds(882, 719, 257, 174);

        btnStart.setBounds(882, 0, 252, 42);
        btnHelp.setBounds(882, 44, 252, 42);
        btnSell.setBounds(882, 88, 252, 42);
        btnReset.setBounds(882, 132, 252, 42);
        
        //archery buttons
        btnUpgradeArchery.setBounds(5, 0, 69, 174);
        btnUArcher.setBounds(79, 0, 84, 84);
        //magic buttons
        btnUpgradeMagic.setBounds(5, 0, 69, 174);
        //trap buttons
        btnUpgradeTraps.setBounds(5, 0, 69, 174);
        //hunter buttons
        btnUpgradeHunters.setBounds(5, 0, 69, 174);

        btnChangeKey1.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                btnUArcher.setMnemonic('Q');
                btnUBlowgunist.setMnemonic('W');
                btnUSpearThrower.setMnemonic('E');
                btnUBallista.setMnemonic('R');
            }
        });
        btnChangeKey2.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                btnUMage.setMnemonic('Q');
                btnUIceSorcerer.setMnemonic('W');
                btnULightDruid.setMnemonic('E');
                btnUEarthElemental.setMnemonic('R');
            }
        });
        
        btnHelp.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                //Execute when button is pressed
                if(normalMouse)
                {
                    normalMouse = false;
                    helpMode    = true;
                }
                else if(!sellMode)
                {
                    normalMouse = true;
                    helpMode    = false;
                    showHelpBox = false;
                }
            }
        });

        btnSell.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(1);
                    return;
                }
                //Execute when button is pressed
                if(normalMouse)
                {
                    normalMouse = false;
                    sellMode    = true;
                }
                else
                {
                    normalMouse = true;
                    sellMode    = false;
                }
            }
        });

        btnReset2.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(0);
                    return;
                }
                
                try {
                    try {
                        //Execute when button is pressed
                        RESET_GAME();
                    } catch (InstantiationException ex) {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } catch (JAXBException ex) {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                } catch (FileNotFoundException ex) {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });

        btnStart.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(0);
                    return;
                }
                //Execute when button is pressed
                waveTimer();
            }
        });

        btnReset.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(2);
                    return;
                }
                try {
                    try {
                        //Execute when button is pressed
                        RESET_GAME();
                    } catch (InstantiationException ex) {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } catch (JAXBException ex) {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                } catch (FileNotFoundException ex) {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });

        //upgrade buttons
        btnUpgradeArchery.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(3);
                    return;
                }
                
                int uAdd = 100;
                int ArcheryLevel = UnitManager.getInstance().getArcheryLevel();

                if(ArcheryLevel !=0 && ArcheryLevel % 5 == 0)
                    UnitManager.getInstance().setArcheryUpCost(uAdd);

                if(decreaseGold(UnitManager.getInstance().getArcheryUpCost()))
                {
                    UnitManager.getInstance().increaseArcheryLevel();
                    updateUpgrades();
                }
            }
        });
        btnUpgradeMagic.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(4);
                    return;
                }
                
                int uAdd = 100;
                int MagicLevel = UnitManager.getInstance().getMagicLevel();
                        
                if(MagicLevel !=0 && MagicLevel % 5 == 0)
                    UnitManager.getInstance().setMagicUpCost(uAdd);

                if(decreaseGold(UnitManager.getInstance().getMagicUpCost()))
                {
                    UnitManager.getInstance().increaseMagicLevel();
                    updateUpgrades();
                }
            }
        });
        btnUpgradeTraps.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(5);
                    return;
                }
                
                int uAdd = 100;
                int TrapLevel = UnitManager.getInstance().getTrapLevel();
                
                if(TrapLevel !=0 && TrapLevel % 5 == 0)
                    UnitManager.getInstance().setTrapUpCost(uAdd);

                if(decreaseGold(UnitManager.getInstance().getTrapUpCost()))
                {
	                UnitManager.getInstance().increaseTrapLevel();
	                updateUpgrades();
                }
            }
        });
        btnUpgradeHunters.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(6);
                    return;
                }
                
                int uAdd = 100;
                int HunterLevel = UnitManager.getInstance().getHunterLevel();
                
                if(HunterLevel !=0 && HunterLevel % 5 == 0)
                    UnitManager.getInstance().setHunterUpCost(uAdd);

                if(decreaseGold(UnitManager.getInstance().getHunterUpCost()))
                {
	                UnitManager.getInstance().increaseHunterLevel();
	                updateUpgrades();
                }
            }
        });

        //archery
        btnUArcher.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(helpMode)
                {
                    showHelp(10);
                    return;
                }
                
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUArcher.getX(), btnUArcher.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Archer"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUBlowgunist.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
            	if(helpMode)
                {
                    showHelp(11);
                    return;
                }
            	
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUBlowgunist.getX(), btnUBlowgunist.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Blowgunist"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUSpearThrower.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
            	if(helpMode)
                {
                    showHelp(12);
                    return;
                }
            	
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUSpearThrower.getX(), btnUSpearThrower.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Spear Thrower"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUBallista.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
            	if(helpMode)
                {
                    showHelp(13);
                    return;
                }
            	
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUBallista.getX(), btnUBallista.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Ballista"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });

        //magic
        btnUMage.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUMage.getX(), btnUMage.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Fire Mage"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUIceSorcerer.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUIceSorcerer.getX(), btnUIceSorcerer.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Ice Sorcerer"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnULightDruid.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnULightDruid.getX(), btnULightDruid.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Light Druid"));
                    units.add(unit);
                }
            }
        });
        btnUEarthElemental.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUEarthElemental.getX(), btnUEarthElemental.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Earth Elemental"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });

        //traps
        btnUSpikes.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit && !attachedTrap)
                {
                    attachedUnit=true;
                    attachedTrap=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUSpikes.getX(), btnUSpikes.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Spikes"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUCatapult.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit && !attachedTrap)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUCatapult.getX(), btnUCatapult.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Catapult"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUDeathSpike.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit && !attachedTrap)
                {
                    attachedUnit=true;
                    attachedTrap=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUDeathSpike.getX(), btnUDeathSpike.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Death Spikes"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUTNT.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit && !attachedTrap)
                {
                    attachedUnit=true;
                    attachedTrap=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUTNT.getX(), btnUTNT.getY()), UnitManager.getInstance().getUnitList().getUnitByName("TNT"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        
        //hunters
        btnUNetter.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUNetter.getX(), btnUNetter.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Netter"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnULancer.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnULancer.getX(), btnULancer.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Lancer"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
        btnUGorillas.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    spawnGorillas();
                }
            }
        });
        btnUDragonSlayer.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                if(!attachedUnit)
                {
                    attachedUnit=true;
                    AttachedUnit unit = new AttachedUnit(new Vector2d(btnUDragonSlayer.getX(), btnUDragonSlayer.getY()), UnitManager.getInstance().getUnitList().getUnitByName("Dragon Slayer"));
                    lastUsed = unit;
                    units.add(unit);
                }
            }
        });
    }

    //DRAWS OUR MAP
    private void initWorld()
    {
        int x = OFFSET;
        int y = OFFSET;

        Wall wall;
        Area area;
        Ground ground;

        for (int i = 0; i < MAP.length(); i++)
        {
            char item = MAP.charAt(i);

            if (item == '\n')
            {
                y += SPACE;
                if (this.w < x)
                {
                    this.w = x;
                }

                x = OFFSET;
            }
            else if (item == '#')
            {
                wall = new Wall(new Vector2d(x, y));
                walls.add(wall);                
                x += SPACE;
            }
            else if (item == ' ')
            {
                ground = new Ground(new Vector2d(x, y));
                grounds.add(ground);
                x += SPACE;
            }
            else if (item == '.')
            {
                area = new Area(new Vector2d(x, y));
                areas.add(area);
                x += SPACE;
            }
        }
    }

    //draws our background art
    private void drawBackArt()
    {
        g = (Graphics2D) strategy.getDrawGraphics();
        ImageIcon bkgi = new ImageIcon(this.getClass().getResource("sprites/castleART.jpg"));
        Image     bkg = bkgi.getImage();
        
        g.drawImage(bkg, 7, 6, null);
        g.dispose();
        strategy.show();
    }
    
    /**
     * Start a fresh game.
     */
    private void startGame() throws JAXBException, FileNotFoundException
    {
        drawBackArt();

        Object[] possibilities = {"easy", "normal", "hard"};
        int response =  JOptionPane.showOptionDialog(
                        null,
                        "Easy: 10 waves\nNormal: 20 waves\nHard: 30 waves\n",
                        "Game difficulty",
                        JOptionPane.YES_OPTION,
                        JOptionPane.PLAIN_MESSAGE,
                        null,
                        possibilities,
                        "normal");
        
        switch (response) 
        {
            case 0: 
                difficulty = "easy";
                break;
            case 1:
                difficulty = "normal";
                break;
            case 2:
                difficulty = "hard";
                break;
            default:
                JOptionPane.showMessageDialog(null, "Goodbye.");
                System.exit(0);
        }
        
        gameRunning = true;
        Enemies.clear();
        initWorld();
        initMenu();
    }

    /**
     * Resets all game variables and starts a new game.
     */
    private void RESET_GAME() throws JAXBException, FileNotFoundException, InstantiationException, IllegalAccessException
    {      
        Enemies       = new ArrayList<Enemy>();
        notPsnEnemies = new ArrayList<Enemy>();
        notSlwEnemies = new ArrayList<Enemy>();

        removeList    = new ArrayList<Enemy>();
        enemyCount    = 0;

        hover         = false;
        hoverMob      = null;
        hoverUnit     = null;

        sellMode      = false;
        normalMouse   = true;
        attachedUnit  = false;
        attachedTrap  = false;

        showHPbars    = true;
        showRange     = false;

        waveTime      = 140;
        gold          = 200;

        grounds       = new ArrayList<Ground>();
        walls         = new ArrayList<Wall>();
        areas         = new ArrayList<Area>();
        buttons       = new ArrayList<Button>();
        units         = new ArrayList<AttachedUnit>();
        placedUnits   = new ArrayList<PlacedUnit>();

        Projectiles   = new ArrayList<Projectile>();

        UnitManager.getInstance().reset();

        sm            = null;

        WaveTimer     = new WaveTimer();
        WaveList      = new WaveList();
        MonsterList   = new MonsterList();

        btnReset2.setBounds(-100, -100, 0, 0);

        //initMenu();
        startGame();
        loadXMLdata();
        updateUpgrades();
        
        if(gamePaused)
            gamePaused = false;
    }

    private void loadXMLdata() throws JAXBException, FileNotFoundException
    {
        //XML
        JAXBContext  wave_list    = JAXBContext.newInstance(WaveList.class);
        JAXBContext  unit_list    = JAXBContext.newInstance(UnitList.class);
        JAXBContext  monster_list = JAXBContext.newInstance(MonsterList.class);
        JAXBContext  help_list    = JAXBContext.newInstance(HelpList.class);
        Unmarshaller wave_unm     = wave_list.createUnmarshaller();
        Unmarshaller unit_unm     = unit_list.createUnmarshaller();
        Unmarshaller monster_unm  = monster_list.createUnmarshaller();
        Unmarshaller help_unm     = help_list.createUnmarshaller();
        
        WaveList    = (WaveList) wave_unm.unmarshal(new FileReader(
                         ("C:\\Users\\Administrator\\Desktop\\CD\\WaveData.xml")));

        MonsterList = (MonsterList) monster_unm.unmarshal(new FileReader(
                         ("C:\\Users\\Administrator\\Desktop\\CD\\MonsterData.xml")));

        UnitManager.getInstance().setUnitList((UnitList) unit_unm.unmarshal(new FileReader(
                         ("C:\\Users\\Administrator\\Desktop\\CD\\UnitData.xml"))));
        
        HelpList    = (HelpList) help_unm.unmarshal(new FileReader(
                         ("C:\\Users\\Administrator\\Desktop\\CD\\HelpData.xml")));
        
        
        
    }

    /**
     * 
     */
    private void waveTimer()
    {
        if(canStartNextWave())
        {
            WaveTimer.start();
            waveTime=1;
            enemyCount=0;
        }
        else
        {
            waveTime=140;
            WaveTimer.stop();
        }
    }

    private boolean canStartNextWave()
    {
        if(difficulty.equals("easy"))
        {
            if(GameManager.getInstance().getCurrentWave() == 10)
                return false;
        }
        else if(difficulty.equals("normal"))
        {
            if(GameManager.getInstance().getCurrentWave() == 20)
                return false;
        }
        else if(difficulty.equals("hard"))
        {
            if(GameManager.getInstance().getCurrentWave() == 30)
                return false;
        }

        return true;
    }
    
    public static WaveList getWaveList()
    {
        return WaveList;
    }

    public static MonsterList getMonsterList()
    {
        return MonsterList;
    }
    
    public static ArrayList<Enemy> getEnemyList()
    {
        return Enemies;
    }

    public static List<PlacedUnit> getPlacedUnitList()
    {
        return placedUnits;
    }
    
    private void startNextWave()
    {
        if(canStartNextWave())
            if(WaveTimer.getElapsedTimeSecs()>=waveTime)
            {
                enemyCount = 0;
                waveTime   = 140;
                GameManager.getInstance().nextWave();
                WaveManager.getInstance().doNextWave();
                WaveTimer.clear();
                WaveTimer.start();
            }
    }

    public void updateUpgrades()
    {
        //archery
        if(UnitManager.getInstance().getArcheryLevel()>19)     //20+
            btnUBallista.setBounds(168, 90, 84, 84);
        else if(UnitManager.getInstance().getArcheryLevel()>6) //7+
            btnUSpearThrower.setBounds(79, 90, 84, 84);
        else if(UnitManager.getInstance().getArcheryLevel()>0) //1+
            btnUBlowgunist.setBounds(168, 0, 84, 84);
        else
        {
            btnUBlowgunist.setBounds(168, 0, 0, 0);
            btnUSpearThrower.setBounds(79, 90, 0, 0);
            btnUBallista.setBounds(168, 90, 0, 0);
        }

        //magic
        if(UnitManager.getInstance().getMagicLevel()>24)      //25+
            btnUEarthElemental.setBounds(168, 90, 84, 84);
        else if(UnitManager.getInstance().getMagicLevel()>14) //15+
            btnULightDruid.setBounds(79, 90, 84, 84);
        else if(UnitManager.getInstance().getMagicLevel()>6)  //7+
            btnUIceSorcerer.setBounds(168, 0, 84, 84);
        else if(UnitManager.getInstance().getMagicLevel()>0)  //1+
            btnUMage.setBounds(79, 0, 84, 84);
        else
        {
            btnUMage.setBounds(79, 0, 0, 0);
            btnUIceSorcerer.setBounds(168, 0, 0, 0);
            btnULightDruid.setBounds(79, 90, 0, 0);
            btnUEarthElemental.setBounds(168, 90, 0, 0);
        }

        //traps
        if(UnitManager.getInstance().getTrapLevel()>19)      //20+
            btnUTNT.setBounds(168, 90, 84, 84);
        else if(UnitManager.getInstance().getTrapLevel()>11) //12+
            btnUDeathSpike.setBounds(79, 90, 84, 84);
        else if(UnitManager.getInstance().getTrapLevel()>5)  //6+
            btnUCatapult.setBounds(168, 0, 84, 84);
        else if(UnitManager.getInstance().getTrapLevel()>0)  //1+
            btnUSpikes.setBounds(79, 0, 84, 84);
        else
        {
            btnUSpikes.setBounds(79, 0, 0, 0);
            btnUCatapult.setBounds(168, 0, 0, 0);
            btnUDeathSpike.setBounds(79, 90, 0, 0);
            btnUTNT.setBounds(168, 90, 0, 0);
        }

        //hunters
        if(UnitManager.getInstance().getHunterLevel()>24)      //25+
            btnUDragonSlayer.setBounds(168, 90, 84, 84);
        else if(UnitManager.getInstance().getHunterLevel()>11) //12+
            btnUGorillas.setBounds(79, 90, 84, 84);
        else if(UnitManager.getInstance().getHunterLevel()>4)  //5+
            btnULancer.setBounds(168, 0, 84, 84);
        else if(UnitManager.getInstance().getHunterLevel()>0)  //1+
            btnUNetter.setBounds(79, 0, 84, 84);
        else
        {
            btnUNetter.setBounds(79, 0, 0, 0);
            btnULancer.setBounds(168, 0, 0, 0);
            btnUGorillas.setBounds(79, 90, 0, 0);
            btnUDragonSlayer.setBounds(168, 90, 0, 0);
        }
    }

    /**
     * Remove an Enemy from the game. The Enemy removed will
     * no longer move or be drawn.
     *
     * @param enemy The Enemy that should be removed
     */
    public static void removeEnemy(Enemy enemy)
    {
        removeList.add(enemy);
        notifyEnemyKilled();
    }

    public static void removeProjectile(Projectile proj)
    {
        Projectiles.remove(proj);
    }

    public static void removeUnit(PlacedUnit unit)
    {
        placedUnits.remove(unit);
    }    
    /**
     * Notification that the player has died.
     */
    public static void notifyDeath()
    {
        String go       = "GAME OVER!";
        g.setFont(new Font("Verdana", Font.PLAIN, 80));
        FontMetrics fm  = g.getFontMetrics();

        int textHeight  = 200;
        int textWidth   = fm.stringWidth(go);

        // Center text horizontally and vertically
        int x = (boardW - textWidth)  / 2;
        int y = (boardH - textHeight) / 2  + fm.getAscent();

        g.setColor(Color.black);
        g.drawString(go, x+4, y+4);
        g.setColor(Color.red);
        g.drawString(go, x, y);

        btnReset2.setBounds(((boardW-130)/2), ((boardH)/2), 130, 35);

        gamePaused = true;
    }

    /**
     * Notification that the player has won since all the aliens
     * are dead.
     */
    public static void notifyWin()
    {
        String go       = "YOU WIN!";
        Graphics2D g2d    = (Graphics2D) strategy.getDrawGraphics();
        g2d.setFont(new Font("Verdana", Font.PLAIN, 80));
        FontMetrics fm  = g2d.getFontMetrics();

        int textHeight  = 200;
        int textWidth   = fm.stringWidth(go);

        // Center text horizontally and vertically
        int x = (boardW - textWidth)  / 2;
        int y = (boardH - textHeight) / 2  + fm.getAscent();

        g2d.setColor(Color.black);
        g2d.drawString(go, x+4, y+4);
        g2d.setColor(new Color(43,153,0));
        g2d.drawString(go, x, y);

        gameRunning = false;
    }
    
    /**
     * Notification that an alien has been killed
     */
    public static void notifyEnemyKilled()
    {
        if(GameManager.getInstance().getDifficulty() == Difficulty.EASY)
        {
            if (Enemies.isEmpty() && GameManager.getInstance().getCurrentWave() == 10)
            {
                notifyWin();
            }
        }
        else if(GameManager.getInstance().getDifficulty() == Difficulty.NORMAL)
        {
            if (Enemies.isEmpty() && GameManager.getInstance().getCurrentWave() == 20)
            {
                notifyWin();
            }
        }
        else if(GameManager.getInstance().getDifficulty() == Difficulty.HARD)
        {
            if (Enemies.isEmpty() && GameManager.getInstance().getCurrentWave() == 30)
            {
                notifyWin();
            }
        }
    }

    public static void addGold(int amt)
    {
        gold += amt;
    }

    public static boolean decreaseGold(int amount)
    {
        if((gold-amount) < 0)
            return false;
        
        gold -= amount;
        
        return true;
    }

    public static int getGold()
    {
        return gold;
    }

    public static void addEnemy(Enemy e)
    {
        Enemies.add(e);
        enemyCount++;
    }

    public static void addPlacedUnit(PlacedUnit pu)
    {
        placedUnits.add(pu);
    }
    
    public static BufferStrategy getStrategy()
    {
        return strategy;
    }

    private void showHelpBox()
    {
        showHelpBox=true;
    }
    
    private void hideHelpBox()
    {
        showHelpBox=false;
    }
    
    private void showHelp(int helpID)
    {
        helpText = HelpList.getHelpById(helpID);
        showHelpBox();
    }
    
    /**
     * 
     * @param e The enemy to update graphics on.
     */
    public void updateStatusGraphics(Enemy e)
    {
        Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();

        if(e.isPoisoned)
        {
            Image img = SpriteStore.get().getSprite("sprites/projectiles/psn_proj.GIF").getImage();
            g2d.drawImage(img, e.getX(), e.getY()+4, null);
        }
        if(e.isSlowed)
        {
            //TODO
        }
        if(e.isSnared)
        {
            Image img;
        
            if(e.getSize().equals("NORMAL"))
            {
                img = SpriteStore.get().getSprite("sprites/projectiles/net42_proj.PNG").getImage();
            }
            else
            {
                img = SpriteStore.get().getSprite("sprites/projectiles/net84_proj.PNG").getImage();
            }
            
            g2d.drawImage(img, e.getX(), e.getY(), null);
            
            g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            g2d.setColor(Color.black);
            g2d.drawString(e.getSnareTime()+"", e.getX()+e.getSprite().getWidth()-7, e.getY()+e.getSprite().getHeight()+6);
            g2d.setColor(Color.white);
            g2d.drawString(e.getSnareTime()+"", e.getX()+e.getSprite().getWidth()-9, e.getY()+e.getSprite().getHeight()+7);
        }
    }

    /**
     * 
     * @param s The string to draw.
     * @param x The X position.
     * @param y The Y position.
     */
    public static void drawString(String s, float x, float y)
    {
        Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
        Font font = new Font("Dialog", Font.BOLD, 12);
        g2d.setColor(new Color(187,16,16,255));
        g2d.setFont(font);
        g2d.drawString(s, x, y);
    }

    /**
     * 
     * @param target The enemy unit that will be fired on.
     * @param parent The placedUnit where this projectile originates from.
     */
    public static void fireAtEnemy(Enemy target, PlacedUnit parent)
    {
        Unit unit = UnitManager.getInstance().getUnitList().getUnitByName(parent.getName());
        Projectiles.add(new Projectile(unit.getSpeed(),
                                       unit.getRange(),
                                       unit.getDamage(),
                                       unit.getStateEffects(),
                                       unit.getProj(),
                                       parent.getX(), 
                                       parent.getY(), 
                                       target, 
                                       parent));
    }

    /**
     * 
     * @param target The PlacedUnit to shoot at.
     * @param parent The Enemy shooting.
     * @param sprRef The projectile string reference.
     */
    public static void fireAtUnit(PlacedUnit target, Enemy parent, String sprRef)
    {
        Projectiles.add(
                new Projectile(2.0, sprRef, parent.getX(), 
                               parent.getY(), parent, target));
    }
    
    /**
     * @return the hoverMob
     */
    public static Enemy getHoverMob()
    {
        return hoverMob;
    }

    public static void setHoverMob(Enemy e)
    {
        hoverMob = e;
    }

    private void doEnemyHover()
    {
        Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();

        if(getHoverMob()!=null)
        {
            g2d.setColor(new Color(0,0,0,150));
            int[] xPoints = {getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)-2,
                             getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)+6,
                             getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)+14};
            int[] yPoints = {getHoverMob().getY()+getHoverMob().getSprite().getHeight()+2,
                             getHoverMob().getY()+getHoverMob().getSprite().getHeight()-7,
                             getHoverMob().getY()+getHoverMob().getSprite().getHeight()+2};
            g2d.fillPolygon(xPoints, yPoints, 3);

            g2d.fillRect(getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)-2,
                       getHoverMob().getY()+getHoverMob().getSprite().getHeight()+2,
                       104, 58);

            g2d.setColor(new Color(255,255,255,150));
            g2d.fillRect(getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2),
                       getHoverMob().getY()+getHoverMob().getSprite().getHeight()+4,
                       100, 54);
            
            //info
            g2d.setColor(new Color(0,0,0,200));
            int ls = 3;
            g2d.drawString(getHoverMob().getName(),
                         getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)+4,
                         getHoverMob().getY()+getHoverMob().getSprite().getHeight()+(ls+14));
            g2d.drawString((int)getHoverMob().getHP()+"/"+(int)getHoverMob().getMaxHP()+" HP",
                         getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)+4,
                         getHoverMob().getY()+getHoverMob().getSprite().getHeight()+(ls+14)*2);
            g2d.drawString((int)getHoverMob().getBounty()+" Bounty",
                         getHoverMob().getX()+(getHoverMob().getSprite().getWidth()/2)+4,
                         getHoverMob().getY()+getHoverMob().getSprite().getHeight()+(ls+14)*3);
        }
        else
        {
            g2d.dispose();
            hover=false;
        }
        //System.out.println(hoverPanel.getLocation());
    }

    private void doUnitHover()
    {
        if(hoverUnit!=null)
        {
        	try
        	{
	            Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
	
	            g2d.setColor(new Color(0,0,0,150));
	            int[] xPoints = {hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)-2,
	                             hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+6,
	                             hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+14};
	
	            int[] yPoints = {hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+2,
	                             hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)-7,
	                             hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+2};
	            g2d.fillPolygon(xPoints, yPoints, 3);
	
	            g2d.fillRect(hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)-2,
	                       hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+2,
	                       104, 75);
	
	            g2d.setColor(new Color(255,255,255,150));
	            g2d.fillRect(hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2),
	                       hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+4,
	                       100, 71);
	
	            //info
	            g2d.setColor(new Color(0,0,0,200));
	            int lineSpace = 3;
	            g2d.drawString(hoverUnit.getName(),
	                         hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+4,
	                         hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+(lineSpace+14));
	            double damage = UpgradeManager.getInstance().getCurrentDamage(hoverUnit);
	            g2d.drawString((int)damage+" Damage",
	                         hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+4,
	                         hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+(lineSpace+14)*2);
	            g2d.drawString(hoverUnit.getMyKills()+" Kills",
	                         hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+4,
	                         hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+(lineSpace+14)*3);
	            
	            if(hoverUnit.getName().equals(""))
	                g2d.drawString(hoverUnit.getUnitEffect(),
	                         hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+4,
	                         hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+(lineSpace+14)*4);
	            else if(hoverUnit.getName().equals(""))
	                g2d.drawString(hoverUnit.getUnitEffect(),
	                         hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+4,
	                         hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+(lineSpace+14)*4);
	            else
	                g2d.drawString(hoverUnit.getUnitEffect(),
	                         hoverUnit.getX()+(hoverUnit.getSprite().getWidth(null)/2)+4,
	                         hoverUnit.getY()+hoverUnit.getSprite().getHeight(null)+(lineSpace+14)*4);
        	}
        	catch(Exception e)
        	{}
        }
        else
        {
            hoverPanel.setSize(0, 0);
            showRange=false;
        }
    }
    

    /**
     * The main game loop. This loop is running during all game
     * play as is responsible for the following activities:
     * <p>
     * - Working out the speed of the game loop to update moves
     * - Moving the game Enemies
     * - Drawing the screen contents (Enemies, text)
     * - Updating game events
     * - Checking Input
     * <p>
     */
    private static Graphics2D g;
    public void startGameLoop()
    {
        long lastLoopTime = System.nanoTime();
        // keep looping round til the game ends
        while (gameRunning)
        {            
            long delta   = System.nanoTime() - lastLoopTime;
            lastLoopTime = System.nanoTime();
            g = (Graphics2D) strategy.getDrawGraphics();
            
            //high graphics settings :)
            g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                    RenderingHints.VALUE_ANTIALIAS_ON);
            g.setRenderingHint(RenderingHints.KEY_RENDERING,
                    RenderingHints.VALUE_RENDER_QUALITY);
            
            //start the wave timer
            startNextWave();
            
            g.setColor(new Color(0,0,0));
            g.fillRect(0, 0, boardW, boardH);

            //Draw background floor tiles
            ImageIcon ii = new ImageIcon(this.getClass().getResource("sprites/ground.PNG"));
            Image bkg    = ii.getImage();
            
            int rowNum   = boardH/42;
            int colNum   = 21;
            for (int row=0;row<rowNum;row++)
            {
                for (int col=0;col<colNum;col++)
                {
                    g.drawImage(bkg, col*42, row*42, null);
                }
            }
            
            ArrayList<Object> world = new ArrayList<Object>();
            world.addAll(grounds);
            world.addAll(walls);
            world.addAll(areas);
            world.addAll(buttons);

            //draw all world objects
            for (int i = 0; i < world.size(); i++)
            {
                Actor item = (Actor) world.get(i);
                g.drawImage(item.getImage(), (int)item.getX(), (int)item.getY(), this);
            }

            if(GameManager.getInstance().getDoorLife() <= 0)
                notifyDeath();
            
            if(!gamePaused)
            {
                //draw all placed units on pUnit list
                for (int u=0;u<placedUnits.size();u++)
                {
                    PlacedUnit unit = (PlacedUnit) placedUnits.get(u);
                    Image unitImg   = unit.getSprite();
                    
                    if(unit.getAnim()!=null)
                        unitImg = unit.getAnim().getImage();
                    
                    if(unit instanceof RockProjectile)
                    {
                        //rocks last one wave, if rocks life is less than wave 
                        //number remove rocks & stop drawing them.
                        RockProjectile rock = (RockProjectile)unit;
                        if(rock.getWaveLife()<GameManager.getInstance().getCurrentWave())
                        {
                            placedUnits.remove(rock);
                        }
                        else
                        {
                            unitImg = rock.getSprite();
                            g.drawImage(unitImg, (int)rock.getLoc().x, (int)rock.getLoc().y, this);
                            rock.doLogic();
                        }
                    }
                    else
                    //flip unit to the right
                    if(unit.getTarget()!=null && unit.getX()<unit.getTarget().getX())
                    {
                        BufferedImage bufferedImage = new BufferedImage(unitImg.getWidth(null), unitImg.getHeight(null), BufferedImage.TYPE_INT_ARGB);

                        Graphics gb = bufferedImage.getGraphics();
                        gb.drawImage(unitImg, 0, 0, null);
                        gb.dispose();

                        AffineTransform tx = AffineTransform.getScaleInstance(-1, 1);
                        tx.translate(-unitImg.getWidth(null), 0);
                        AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
                        bufferedImage = op.filter(bufferedImage, null);

                        g.drawImage(bufferedImage, null, (int)unit.getLoc().x, (int)unit.getLoc().y);
                    }
                    else
                        g.drawImage(unitImg, (int)unit.getLoc().x, (int)unit.getLoc().y, this);
                    
                    unit.doLogic();
                    
                    if(unit.getName().equals("TNT"))
                        if(((UnitTrap)unit).isTNTActive())
                            placedUnits.remove(unit);
                }

                // cycle round drawing all the enemies we have in the game
                for (int i=0;i<Enemies.size();i++)
                {
                    Enemy mob = (Enemy) Enemies.get(i);
                    mob.draw(g);
                    mob.move(delta);
                    updateStatusGraphics(mob);
                    StateManager.getInstance().updateStateEffects(mob);

                    if(showHPbars)
                    {
                        g.setColor(Color.black);
                        g.fillRect(mob.getX()+7, mob.getY()-3, mob.getHPBB(), 6);
                        g.setColor(Color.red);
                        g.fillRect(mob.getX()+8, mob.getY()-2, mob.getHPBR(), 4);
                    }
                }

                if(showRange)
                {
                    drawRangeIndicator();
                    doUnitHover();
                }
                
                if(hover)
                {
                    doEnemyHover();
                }
                //draw the unit attached to the mouse as long as it is attached
                if(attachedUnit)
                {
                    for (Actor unit : units)
                        g.drawImage(unit.getImage(), mouseX, mouseY, this);
                    
                }
                else if(attachedNet)
                {
                    Image net = new ImageIcon(this.getClass().getResource(
                            GameManager.getInstance().PROJECTILES_PATH()+"net42_proj.PNG")).getImage();
                    g.drawImage(net, mouseX, mouseY, this);
                }
                else if(attachedRock)
                {
                    Image rock = new ImageIcon(this.getClass().getResource(
                            GameManager.getInstance().PROJECTILES_PATH()+"rock_proj.PNG")).getImage();
                    g.drawImage(rock, mouseX, mouseY, this);
                }

                //cycle round moving all the projectiles we have in the game
                //cycle round drawing all the projectiles we have in the game
                for (int i=0;i<Projectiles.size();i++)
                {
                    Projectile proj = (Projectile) Projectiles.get(i);

                    proj.moveToTarget();
                    proj.draw(g);
                }

                // do enemy logic
                for (int i=0;i<Enemies.size();i++)
                {
                    Enemy Enemy = (Enemy) Enemies.get(i);
                    Enemy.EM.doEnemyLogic();
                }
                
                // remove any Enemy that has been marked for clean up
                Enemies.removeAll(removeList);
                removeList.clear();
                
                if(showHelpBox)
                {
                    HelpBox hb = new HelpBox(helpText, null, boardW, boardH, g);
                    BufferedImage helpBox = hb.getHelpBox();

                    g.drawImage(helpBox, null, 0, 0);
                }
            }
            
            //draw the players gold at all times
            Font f = new Font("Dialog", Font.PLAIN, 14);
            g.setFont(f);
            g.setColor(Color.WHITE);

            //upgrades
            g.drawString("Archery: Lv."+UnitManager.getInstance().getArcheryLevel(), 890, 910);
            g.drawString("Magic:    Lv."+UnitManager.getInstance().getMagicLevel(), 890, 926);
            g.drawString("Traps:    Lv."+UnitManager.getInstance().getTrapLevel(), 890, 942);
            g.drawString("Hunters: Lv."+UnitManager.getInstance().getHunterLevel(), 890, 958);

            //Wave/Time/Gold
            g.drawString("Time:  "+(waveTime-WaveTimer.getElapsedTimeSecs()), 1040, 910);
            g.drawString("Wave:  "+GameManager.getInstance().getCurrentWave(), 1040, 926);
            g.drawString("Gold:  "+getGold(), 1040, 942);
            g.drawString("Door:  "+(int)GameManager.getInstance().getDoorLife(), 1040, 958);

            // finally, we've completed drawing so clear up the graphics
            // and flip the buffer over
            g.dispose();
            strategy.show();

            // resolve the movement of the ship. First assume the ship
            // isn't moving. If either cursor key is pressed then
            // update the movement appropraitely

            // finally pause for a bit. Note: this should run us at about
            // 100 fps but on windows this might vary each loop due to
            // a bad implementation of timer
            try { Thread.sleep(10); } catch (Exception e) {}
            
        }
    }

    private Point checkForWall(int x, int y)
    {
        Wall wall;
        double x1,x2,y1,y2;

        for(int c=0;c<walls.size();c++)
        {
            wall = (Wall)walls.get(c);
            
            x1 = wall.getX();
            x2 = wall.getX()+42;
            y1 = wall.getY();
            y2 = wall.getY()+42;

            if(x+25 >= x1 && x+25 < x2 && y+25 >= y1 && y+25 < y2)
                return new Point((int)x1, (int)y1);
        }

        return null;
    }

    private Point checkForGround(int x, int y)
    {
        Ground ground;
        double x1,x2,y1,y2;

        for(int c=0;c<grounds.size();c++)
        {
            ground = (Ground)grounds.get(c);

            x1 = ground.getX();
            x2 = ground.getX()+42;
            y1 = ground.getY();
            y2 = ground.getY()+42;

            if(x+25 >= x1 && x+25 < x2 && y+25 >= y1 && y+25 < y2)
                return new Point((int)x1, (int)y1);
        }

        return null;
    }

    private PlacedUnit checkIfUnitClicked(int x, int y)
    {
        PlacedUnit unit;
        double x1,x2,y1,y2;
        x+=25;
        y+=25;
        
        for(int c=0;c<placedUnits.size();c++)
        {
            unit = (PlacedUnit)placedUnits.get(c);

            x1 = unit.getX();
            x2 = unit.getX()+unit.getSprite().getWidth(null);
            y1 = unit.getY();
            y2 = unit.getY()+unit.getSprite().getHeight(null);

            if(x >= x1 && x < x2 && y >= y1 && y < y2)
                return unit.getPUnit();
        }

        return null;
    }

    public static PlacedUnit checkForUnit(int x, int y)
    {
        PlacedUnit unit;
        double x1,x2,y1,y2;
        //x+=25;
        //y+=25;
        
        for(int c=0;c<placedUnits.size();c++)
        {
            unit = (PlacedUnit)placedUnits.get(c);

            x1 = unit.getX();
            x2 = unit.getX()+unit.getSprite().getWidth(null);
            y1 = unit.getY();
            y2 = unit.getY()+unit.getSprite().getHeight(null);

            if(x >= x1 && x < x2 && y >= y1 && y < y2)
                return unit.getPUnit();
        }

        return null;
    }
    
    public static Enemy checkForEnemy(int x, int y)
    {
        Enemy enemy;
        double x1,x2,y1,y2;

        for(int c=0;c<Enemies.size();c++)
        {
            enemy = (Enemy)Enemies.get(c);
            
            x1 = enemy.getX();
            x2 = enemy.getX()+enemy.getSprite().getWidth();
            y1 = enemy.getY();
            y2 = enemy.getY()+enemy.getSprite().getHeight();

            if(x >= x1 && x < x2 && y >= y1 && y < y2)
                return enemy.getEnemy();
        }

        return null;
    }

    public Enemy checkIfEnemyClicked(int x, int y)
    {
        Enemy enemy;
        double x1,x2,y1,y2;
        x+=25;
        y+=25;
        
        for(int c=0;c<Enemies.size();c++)
        {
            enemy = (Enemy)Enemies.get(c);

            x1 = enemy.getX();
            x2 = enemy.getX()+enemy.getSprite().getWidth();
            y1 = enemy.getY();
            y2 = enemy.getY()+enemy.getSprite().getHeight();

            if(x >= x1 && x < x2 && y >= y1 && y < y2)
                return enemy.getEnemy();
        }

        return null;
    }

    private void drawRangeIndicator()
    {
        if(hoverUnit==null || hoverUnit.getUnitClass().equals("Traps"))
            return;
        
        int x,y,radius;
        x      = (int)hoverUnit.getMidX();
        y      = (int)hoverUnit.getMidY();
        radius = (int)hoverUnit.getRange()-21;

        Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
        g2d.setColor(Color.black);
        g2d.setStroke(new BasicStroke(2f));
        g2d.drawRect((int)hoverUnit.getX()-1, (int)hoverUnit.getY()-1,
                   (int)hoverUnit.getSprite().getWidth(null),
                   (int)hoverUnit.getSprite().getHeight(null));
        g2d.drawOval(x-radius, y-radius, radius*2, radius*2);
        g2d.setColor(new Color(255,255,255,50));
        g2d.fillOval(x-radius, y-radius, radius*2, radius*2);
    }

    private void spawnGorillas() 
    {
                
    }
    
    private class MouseCursorListener extends MouseAdapter
    {
        @Override
        public void mouseEntered(MouseEvent e)
        {
            if(normalMouse)
            {
                //  Set cursor for the frame component
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                BufferedImage  image = null;

                try
                {
                    image = ImageIO.read(this.getClass().getResource("sprites/Arrow.gif"));
                }
                catch (IOException ex)
                {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                }
                Cursor c = toolkit.createCustomCursor(image , new Point(panel.getX(),panel.getY()), "img");
                panel.setCursor (c);
            }
            else if(sellMode)
            {
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                BufferedImage  image = null;
                try
                {
                    image = ImageIO.read(this.getClass().getResource("sprites/Sell_unit.GIF"));
                }
                catch (IOException ex)
                {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                }
                Cursor c = toolkit.createCustomCursor(image , new Point(panel.getX(),panel.getY()), "img2");
                panel.setCursor (c);
            }
            else if(helpMode)
            {
                Toolkit toolkit = Toolkit.getDefaultToolkit();
                BufferedImage  image = null;
                try
                {
                    image = ImageIO.read(this.getClass().getResource("sprites/Help.GIF"));
                }
                catch (IOException ex)
                {
                    Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                }
                Cursor c = toolkit.createCustomCursor(image , new Point(panel.getX(),panel.getY()), "img3");
                panel.setCursor (c);
            }
        }

        @Override
        public void mouseClicked(MouseEvent e)
        {
            if(e.getModifiers()==InputEvent.BUTTON3_MASK)
            {
                units.clear();

                netter   = null;
                catapult = null;

                attachedUnit = false;
                attachedTrap = false;
                showRange    = false;
                attachedNet  = false;
                attachedRock = false;
                normalMouse  = true;
            }
            
            if(attachedUnit || attachedTrap)
            {
                //right click
                if(e.getModifiers()==InputEvent.BUTTON1_MASK) //left click
                {
                    if(attachedUnit && !attachedTrap)
                    {
                        //get the units sprite
                        AttachedUnit attachUnit = (AttachedUnit)units.get(units.size()-1);
                        int unitCost  = UnitManager.getInstance().getUnitList().getUnitByName(attachUnit.getName()).getCost();
                        if(!decreaseGold(unitCost))
                        {
                            units.clear();
                            attachedUnit = false;
                            normalMouse = true;
                            return;
                        }

                        //get the x,y point of the unit
                        Point p = checkForWall(mouseX, mouseY);
                        //if p is null, there is no unit, break operation
                        if(p==null)
                            return;

                        //check if theres a unit on the wall
                        PlacedUnit u = checkIfUnitClicked(mouseX, mouseY);
                        //if there is return
                        if(u!=null)
                            return;

                        //create a new unit, Placed Unit
                        PlacedUnit pu = new PlacedUnit(new Vector2d(p.x,p.y), attachUnit.getImage(), attachUnit.getName());
                        
                        if(pu.getName().equals("Archer"))
                        {
                            Archer newUnit = new Archer(new Vector2d(p.x,p.y), attachUnit.getImage());
                            placedUnits.add(newUnit);
                        }
                        else if(pu.getName().equals("Blowgunist"))
                        {
                            Blowgunist newUnit = new Blowgunist(new Vector2d(p.x,p.y), attachUnit.getImage());
                            placedUnits.add(newUnit);
                        }
                        else if(pu.getName().equals("Spear Thrower"))
                        {
                            SpearThrower newUnit = new SpearThrower(new Vector2d(p.x,p.y), attachUnit.getImage());
                            placedUnits.add(newUnit);
                        }
                        else if(pu.getName().equals("Ballista"))
                        {
                            Ballista newUnit = new Ballista(new Vector2d(p.x,p.y), attachUnit.getImage());
                            placedUnits.add(newUnit);
                        }
                        else if(pu.getUnitClass().equals("Magic"))
                        {
                            UnitMage newUnit = new UnitMage(new Vector2d(p.x,p.y), attachUnit.getImage(), attachUnit.getName());
                            placedUnits.add(newUnit);
                        }
                        else if(pu.getUnitClass().equals("Traps"))
                        {
                            UnitTrap newUnit = new UnitTrap(new Vector2d(p.x,p.y), attachUnit.getImage(), attachUnit.getName());
                            placedUnits.add(newUnit);
                        }
                        else if(pu.getUnitClass().equals("Hunter"))
                        {
                            UnitHunter newUnit = new UnitHunter(new Vector2d(p.x,p.y), attachUnit.getImage(), attachUnit.getName());
                            placedUnits.add(newUnit);
                        }

                        attachedUnit = false;
                        units.clear();
                    }
                    else if(attachedTrap)
                    {
                        //get the units sprite
                        AttachedUnit au = (AttachedUnit)units.get(units.size()-1);
                        int unitCost  = UnitManager.getInstance().getUnitList().getUnitByName(au.getName()).getCost();
                        if(getGold()<unitCost)
                        {
                            attachedUnit=false;
                            normalMouse=true;
                            return;
                        }

                        //get the x,y point of the unit
                        Point p = checkForGround(mouseX, mouseY);
                        //if p is null, there is no unit, break operation
                        if(p==null)
                            return;

                        decreaseGold(unitCost);

                        //create a new unit, Placed Unit
                        UnitTrap newUnit = new UnitTrap(new Vector2d(p.x,p.y), au.getImage(), au.getName());
                        placedUnits.add(newUnit);

                        attachedUnit = false;
                        attachedTrap = false;
                        normalMouse  = true;
                        units.clear();
                    }
                }
            }
            else if(sellMode)
            {
                showRange = false;
                hoverUnit = null;

                if(e.getModifiers()==InputEvent.BUTTON1_MASK)
                {
                    //sell mode
                    PlacedUnit soldUnit = checkIfUnitClicked(mouseX,mouseY);
                    if(soldUnit!=null)
                    {
                        addGold((int)soldUnit.getSaleValue());
                        removeUnit(soldUnit);
                    }
                }
            }
            else
            {
                if(!normalMouse)
                    return;  
                
                if(e.getModifiers()==InputEvent.BUTTON1_MASK)
                {
                    PlacedUnit clickedUnit  = checkIfUnitClicked(mouseX, mouseY);
                    Enemy      enemy        = checkIfEnemyClicked(mouseX, mouseY);
                    
                    if(clickedUnit instanceof RockProjectile)
                        return;
                    
                    if(!Null(netter))
                    {
                        if(!Null(enemy))
                        {
                            netter.setAttachedNet(false);
                            if(netter.hasNets())
                            {
                                netter.removeNet();
                                enemy.addState(StateEffect.SNARE);
                            }
                            else
                            {
                                netter       = null;
                                attachedNet  = false;
                            }
                        }
                        return;
                    }
                    
                    if(!Null(catapult))
                    {
                        Point gp = checkForGround(mouseX,mouseY);
                        if(Null(gp))
                            return;
                        
                        catapult.setAttachedRock(false);
                        if(catapult.hasRocks())
                        {
                            catapult.removeRock();

                            //create boulder
                            double trapDamage      = UpgradeManager.getInstance().getCurrentDamage(catapult);
                            Image rockProj         = SpriteStore.get().getSprite(
                                    GameManager.getInstance().PROJECTILES_PATH()+"rock_proj.PNG").getImage();
                            RockProjectile boulder = new RockProjectile(
                                    trapDamage, new Vector2d(gp.x,gp.y), rockProj, GameManager.getInstance().getCurrentWave(), catapult);
                            addPlacedUnit(boulder);
                        }
                        return;
                    }

                    //unit clicked
                    if(Null(clickedUnit))
                    {
                        showRange = false;
                        hoverUnit = null;
                    }
                    else
                    {
                        if(clickedUnit.getName().equals("Netter"))
                        {  
                            netter      = (UnitHunter)clickedUnit;
                            
                            if(!netter.hasNets())
                                return;
                            
                            attachedNet = true;
                            hoverUnit   = clickedUnit;
                            netter.setAttachedNet(true);
                            
                            return;
                        }
                        else if(clickedUnit.getName().equals("Catapult"))
                        {
                            catapult     = (UnitTrap)clickedUnit;
                            
                            if(!catapult.hasRocks())
                                return;
                            
                            attachedRock = true;
                            hoverUnit    = clickedUnit;
                            catapult.setAttachedRock(true);
                            return;
                        }
                        
                        hoverUnit = clickedUnit;
                        showRange = true;
                    }

                    //monster clicked
                    if (enemy != null)
                    {
                        hoverMob = enemy;
                        hover    = true;
                    }
                    else
                    {
                        hoverMob = null;
                    }
                }
            }
        }
    }
    
    private boolean Null(Object o)
    {
        if(o!=null)
            return false;
        else
            return true;
    }

    public static Graphics2D getGameGraphics()
    {
    	return g;
    }
    
    private class MouseMovementListener extends MouseAdapter
    {
        @Override
        public void mouseMoved(MouseEvent e)
        {
            mouseX = e.getX()-25;
            mouseY = e.getY()-25;

            if(normalMouse)
            {
                if(checkIfEnemyClicked(mouseX,mouseY)!=null)
                {
                    //  Set cursor for the frame component
                    Toolkit toolkit = Toolkit.getDefaultToolkit();
                    BufferedImage  image = null;

                    try
                    {
                        image = ImageIO.read(this.getClass().getResource("sprites/Arrow_enemy.gif"));
                    }
                    catch (IOException ex)
                    {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    Cursor c = toolkit.createCustomCursor(image , new Point(panel.getX(),panel.getY()), "img");
                    panel.setCursor (c);
                }
                else if(checkIfUnitClicked(mouseX,mouseY)!=null)
                {
                    //  Set cursor for the frame component
                    Toolkit toolkit = Toolkit.getDefaultToolkit();
                    BufferedImage  image = null;

                    try
                    {
                        image = ImageIO.read(this.getClass().getResource("sprites/Arrow_unit.gif"));
                    }
                    catch (IOException ex)
                    {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    Cursor c = toolkit.createCustomCursor(image , new Point(panel.getX(),panel.getY()), "img");
                    panel.setCursor (c);
                }
                else
                {
                    //  Set cursor for the frame component
                    Toolkit toolkit = Toolkit.getDefaultToolkit();
                    BufferedImage  image = null;

                    try
                    {
                        image = ImageIO.read(this.getClass().getResource("sprites/Arrow.gif"));
                    }
                    catch (IOException ex)
                    {
                        Logger.getLogger(Game.class.getName()).log(Level.SEVERE, null, ex);
                    }
                    Cursor c = toolkit.createCustomCursor(image , new Point(panel.getX(),panel.getY()), "img");
                    panel.setCursor (c);
                }
            }
        }
    }

    private class KeyboardListener implements KeyListener
    {

        @Override
        public void keyTyped(KeyEvent e) 
        {
            //
        }

        @Override
        public void keyPressed(KeyEvent e) 
        {
            //
        }

        @Override
        public void keyReleased(KeyEvent e) 
        {
            if(e.getKeyCode() == KeyEvent.VK_SHIFT)
            {
                if(lastUsed != null)
                {
                    attachedUnit=true;
                    if(lastUsed.isGroundTrap())
                        attachedTrap=true;
                    
                    units.add(lastUsed);
                }
            }
            else if(e.getKeyCode() == KeyEvent.VK_SPACE)
            {
                waveTimer();
            }
                
        }
    }
    
    /**
     * The entry point into the game. We'll simply create an
     * instance of class which will start the display and game
     * loop.
     *
     * @param argv The arguments that are passed into our game
     */
    public static void main(String argv[]) throws JAXBException, FileNotFoundException
    {
            Game castleDefense = new Game();
            /*
             Start the main game loop, note: this method will not
             return until the game has finished running. Hence we are
             using the actual main thread to run the game.
            */
            castleDefense.startGameLoop();
    }
}
